from core.itertools2 import listify
from plyny.plio.files import Path, open_path, Directory, File

import re


class WorkstreamError(Exception):
    pass


class _BasePath(object):
    def __init__(self, _dir,  path, workstream_manager):
        self._dir = _dir
        self._path = path
        self._wm = workstream_manager

    @classmethod
    def create_with_abspath(cls, workstream_dir, path, wm, executions=None):
        if isinstance(path, basestring):
            path = Path(path)

        path = path.abs() % workstream_dir.path().abs()
        return cls.create_with_path(workstream_dir, path, wm,
                executions)

    @classmethod
    def create_with_path(cls, workstream_dir, path, wm=None, executions=None):
        if executions is None:
            toks = unicode(path).split('#')

            if len(toks) > 2:
                raise ValueError('XXX')

            if len(toks) == 1:
                toks += [None]
            else:
                toks[1] = toks[1].split(',')

            path, executions = toks

        if isinstance(path, basestring):
            path = Path(path)
#        self._wsdir = workstream_dir

        args = [workstream_dir, path, wm]
        if executions:
            return args.append(executions)

        return cls(*args)

    def size(self):
        return open_path(self.file_path()).size()

    def exists(self):
        return open_path(self.file_path()).exists()

    def __unicode__(self):
        return unicode(self._path)

    def __repr__(self):
        return unicode(self._path)

    def workstream_path(self):
        return self._path

    def file_path(self):
        return self._dir.path() + self._path

    def _list_files(self):
        try:
            return Directory(self.file_path()).list_names().ignore_dot().by_name()
#            return sorted(Directory(self.file_path()).list_names().ignore_dot().by_name())
        except OSError as e:
            return []


class _Iter(object):
    def substream_as_path(self):
        return Path('/'.join(self._path.components()[:-2]))

    def process(self):
        return self._path.components()[0]


class ExecutionWorkstreamPath(_BasePath, _Iter):
    def compressed(self):
        ext = self._path.extension()
        if ext is None:
            return False
        return ext.lower() == 'gz'

    def open(self, *args, **kw):
        return self._wm.open(self.file_path())

    def file_path(self):
        return super(ExecutionWorkstreamPath, self).file_path() + '/output'

    def name(self):
        for c in reversed(self._path.components()):
            if self.isiteration(c):
                continue
            return c

    def isiteration(self, component):
        import re
        return re.match('^\d{4}(\.\d{2}){5}', component)

    def iteration(self):
        for component in self._path.components():
            if self.isiteration(component):
                return component
        raise ValueError('no component found')

#    def output(self):
#        return self._path.components()[-1]

    def substreams(self):
        return self._path.components()[1:-2]


class IterationWorkstreamPath(_BasePath, _Iter):
    def current(self):
        return self.iteration(0)

    def iteration(self, number=0):
        items = list(self._list_files())
        has_current = False
        for i in items:
            has_current = has_current or i.split('/')[-1] == 'current'  # XXX hackish

        if has_current:
            index = -2 + number
        else:
            index = -1 + number

        return ExecutionWorkstreamPath.create_with_abspath(self._dir,
                items[index], self._wm)

    def previous(self):
        items = list(self._list_files())
        return [ExecutionWorkstreamPath.create_with_abspath(self._dir, x,
            self._wm) for x in items[:-2]]

    def date(self):
        import datetime
        return datetime.datetime(*map(int, self._path.components[-1].split('.')))

    def substreams(self):
        return self._path.components()[1:]


class ExpandedWorkstreamPath(_BasePath):
    @listify
    def workstreams(self):
        if self.is_workstream():
            yield IterationWorkstreamPath(self._dir, self._path, self._wm)
        else:
            for w in self.files():
                if w.is_workstream():
                    yield IterationWorkstreamPath(w._dir, w._path, self._wm)
                else:
                    for item in w.workstreams():
                        yield IterationWorkstreamPath(item._dir, item._path, self._wm)

    @listify
    def files(self):
        for item in self._list_files():
            yield ExpandedWorkstreamPath.create_with_abspath(self._dir, item,
                    self._wm)

    def current(self):
        iterations = []
        for w in self.workstreams():
            iterations.append(w.current())

        return iterations

    def substreams(self):
        if self.is_workstream():
            return []

        subs = []
        seen = set()
        for w in self.files():
            if w in seen:
                continue

            if w.is_workstream():
                subs.append(self)
                return subs  # already added ourselves ... XXX
            else:
                subs.append(w)
        return subs

    def is_workstream(self):
        try:
            return len(self._ws_instances()) > 0
        except OSError:
            return False

    def _ws_instances(self):
        return filter(lambda x:
                re.match('^\d\d\d\d\.\d\d\.\d\d\.\d\d\.\d\d\.\d\d$',
                    x.path().basename()), (File(x) for x in
                        Directory(self.file_path()).list_names()
                        .ignore_dot().by_date()))


class WorkstreamPath(_BasePath):
    def __init__(self, _dir, path, ws, executions=None):
        super(WorkstreamPath, self).__init__(_dir, path, ws)
        self._executions = executions

#    def __hash__(self):
#        return hash(self._path)

    def workstreams(self):
        b = self.branches()
        workstreams = []
        for bi in b:
            for ws in bi.workstreams():
                workstreams.append(ws)

        return workstreams

    def is_workstream(self):
        l = self.workstreams()
        return len(l) == 1 and l[0] is self

    def substreams(self):
        substreams = []
        for bi in self.branches():
            for sb in bi.substreams():
                substreams.append(sb)

        return substreams

    def current(self):
        iterations = []
        for w in self.workstreams():
            iterations.append(w.current())

        return iterations

    def branches(self):
        b = []
        for i, item in enumerate(self._path.components()):
            c = ','
            if c in item:
                b.append(item.split(c))
            elif item == '*':
                d = self._dir / Path.join(self._path.components()[:i])
                b.append([x.path().leaf() for x in d.list_names().files()])
            else:
                b.append(None)

        paths = [[]]
        for i, item in enumerate(self._path.components()):
            if b[i] is not None:
                new_paths = []
                for component in b[i]:
                    for path in paths:
                        new_paths.append(path + [component])
                paths = new_paths
            else:
                for path in paths:
                    path.append(item)

        return [ExpandedWorkstreamPath.create_with_path(self._dir, '/'.join(x),
            self._wm, self._executions) for x in sorted(paths)]
